import {EE, prompt, storage} from "@/utils";
import {EnumChartType} from './constants/EnumChartType';
import {getBasicChartOptions} from '@/pages/dv/constants/EnumBasicChartOptions';
import chartRoutes from './charts';
import EnumOnFire from "@/pages/dv/constants/EnumOnFire";
import _, {isEmpty, merge, isString, isPlainObject, cloneDeep} from 'lodash';
import DvState from './dvState';
import Mousetrap from 'mousetrap';
import {nanoid} from 'nanoid';

/**
 * dv操作函数库
 */
class DvTool extends DvState{
  public chartsContainer: any;
  public chartsInstance: any;
  public $cssLink: any;


  public MouseAction: {
    bind: Function;
    unbind: Function;
    reset: Function;
  };

  constructor() {
    super();
    this.chartsContainer = {};
    this.chartsInstance = {};
    this.MouseAction = Mousetrap(document.body);

    this.exportDv();
    this.getChartInstance = this.getChartInstance.bind(this)

  }

  exportDv(){
    window.dv = {
      ...this.getPublicMethods()
    }
  }


  /**
   * 获取选中的组件的配置
   */
  getSelectedChartOption(selectedChartKey?: string): DV.IChart {
    if(selectedChartKey){
      return this.charts[selectedChartKey] || {}
    }
    return this.charts[this.selectedChartKey] || {}
  }

  /**
   * 移动组件位置
   * @param x
   * @param y
   */
  moveChartPosition({x = 0, y = 0}){
    if(this.selectedChartKey){
      const selectedChart: any = this.charts[this.selectedChartKey];
      this.updateBaseConfig(this.selectedChartKey, {
        left: selectedChart.left + x,
        top: selectedChart.top + y,
      })
    }
  }

  getUid(size = 10){
    return nanoid(size)
  }

  /**
   * 添加组件
   */
  addChartOption(type: DV.IChartType, option: DV.IChart, isDrawEnd: boolean){
    const {label, frameType, configTemp, defaultConfig} = chartRoutes[type];
    const uid = frameType + '_' + this.getUid(10);
    const chartConfig = merge(defaultConfig || {}, getBasicChartOptions(configTemp), {
      type,
      name: label,
      frameType,
      uid,
      zIndex: this.maxZIndex + 1,
      ...option,// 尺寸和位置
    });
    this.addChart(uid, chartConfig, () => {
      isDrawEnd && EE.on(EnumOnFire.cacheChartInstance, (cacheUid: string) => {
        // 挂载完成才能切换选中组件
        cacheUid === uid && this.selectedChart(uid);
      })
    })
  }

  /**
   * 添加组件
   */
  copyChartOption(uid: string, option: DV.IChart, isDrawEnd: boolean){
    if(!uid || !this.charts[uid]) {
      prompt.error('复制失败！')
      return;
    }
    const newOption = this.charts[uid];
    const newUid = newOption.frameType + '_' + this.getUid(10);
    const chartConfig = merge({}, cloneDeep(newOption), {
      uid: newUid,
      zIndex: this.maxZIndex + 1,
      ...option,// 尺寸和位置
    });
    this.addChart(newUid, chartConfig, () => {
      isDrawEnd && EE.on(EnumOnFire.cacheChartInstance, (cacheUid: string) => {
        // 挂载完成才能切换选中组件
        cacheUid === newUid && this.selectedChart(newUid);
      })
    })
  }

  /**
   * 移除组件
   */
  removeChart(uid: string){
    // 关联绑定事件检查

    // 执行删除动作
    if(this.selectedChartKey === uid)  this.selectedChartKey = '';
    this.charts.hasOwnProperty(uid) && delete this.charts[uid];
    this.chartsInstance.hasOwnProperty(uid) && delete this.chartsInstance[uid];
    EE.fire(EnumOnFire.deleteChart, uid);
  }

  /**
   * 复制组件
   */
  copyChart(uid: string){

  }



  /**
   * 分割代码
   * @param key
   */
  splitConfig(key?: any) {
    let chartConfig = {};
    if (isString(key)) chartConfig = this.getSelectedChartOption(key)
    if (isPlainObject(key)) chartConfig = key;
    if (isEmpty(chartConfig)) chartConfig = this.getSelectedChartOption()
    const {
      config,
      dataConfig,
      events,
      codeConfig,
      ...baseConfig
    } = chartConfig;

    return Object.assign({}, {
      baseConfig,
      advanceConfig: config,
      dataConfig,
      codeConfig,
      events
    })
  }

  /**
   * 获取组件容器
   */
  getContainer(uid: string) {
    return this.getChartInstance(uid).box;
  }

  /**
   * 缓存组件实例
   */
  cacheChartInstance(uid: string, chart: DV.IChartInstance) {
    this.chartsInstance[uid] = merge(chart, {
      data: null,
      chart: {
        getOption: () => this.getChartConfig(uid)
      }
    });
    EE.fire(EnumOnFire.cacheChartInstance, uid, this.chartsInstance[uid])
  }

  /**
   * 获取组件实例
   */
  getChartInstance = (uid: string = this.selectedChartKey):DV.IChartInstance =>  {
    return this.chartsInstance[uid] || {}
  }

  /**
   * 清空容器
   */
  clearChartInstance() {
    this.chartsInstance = {};
  }

  getPublicMethods(){
    return {
      getChartInstance: this.getChartInstance, // 获取组件容器
      getUid: this.getUid, // 获取uuid
    }
  }

  /**
   * 获取组件属性配置
   * @param uid
   * @param option
   */
  getChartConfig(uid: string, option: DV.IChart = {}): DV.IChart{
    return merge(this.charts[uid] || {}, option)
  }

  /**
   * 获取参数
   */
  getArguments(uid: string) {
    return {
      ...this.getChartInstance(uid),
      _,
      dv: {
        ...this.getPublicMethods()
      },
    }
  }

  /**
   * 更新加载组件
   * @param chartOption
   * @param onOk
   * @param onError
   */
  reloadChart(chartOption: DV.IChart = {}, onOk:  () => void, onError: (e:Error) => void){
    if(!chartOption.uid) return;
    const {code = ''} = chartOption.codeConfig || {};
    const args = this.getArguments(chartOption.uid);
    if(!code.trim()){
      // 如果代码为空则直接reload
      args.chart.reload && args.chart.reload();
      onOk && onOk();
    } else {
      try {
        // data 数据
        // myChart 图表实例
        // box  容器
        // chart 组件配置
        // dv 大屏函数工具
        (
          new Function(...['data', 'myChart', 'box', 'chart', 'dv', '_'], code)
            .bind({}, args.data, args.myChart, args.box, args.chart, args.dv, _)
        )()

        onOk && onOk();
      } catch (e) {
        console.error('组件['+chartOption.name+']编程运行错误: ' + e.message);
        onError && onError(e);
      }
    }
  }
  /**
   * 更新css
   * @param cssCode
   * @param onOk
   * @param onError
   */
  reloadCss(cssCode: string, onOk:  () => void, onError: (e:Error) => void){
    try {
      if(this.$cssLink){
        this.$cssLink.innerText = cssCode;
      } else{
        this.$cssLink = document.createElement('style');
        this.$cssLink.type = 'text/css';
        document.head.appendChild(this.$cssLink);
        this.$cssLink.innerText = cssCode;
      }
      onOk && onOk();
    } catch (e) {
      console.error('CSS代码挂载错误: ' + e.message);
      onError && onError(e);
    }
  }

}

export default new DvTool();
